home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / eval_az.com / EVALUATE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-07-13  |  27.6 KB  |  958 lines

  1. {$V-}
  2. {---------------------------------------------------------------------
  3.  EVALUATOR UNIT TEST PROGRAM
  4.  Arthur Zatarain, P.E.        C'Serve 3417-525    Bixen=ARTZAT
  5.  Total Engineering Services Team, Inc.  (TEST Inc).
  6.  New Orleans,  La.  (504) 368-6792 Days
  7.                           837-3699 Nites
  8.  
  9. This expression evaluator is based on a
  10. Compuserve Upload by Neil J. Rubenking.
  11.  
  12. This program takes a legal arithmetic expression in the form of a
  13. string and evaluates it.
  14.  
  15. Binary operators are +-/* ^, unary operators are - and %,
  16. where prefixing an expression in parentheses with a %
  17. causes it to be truncated to an integer.  To get #A mod #B,
  18. you could do  "#A - (%(#A/#B)*#B)"
  19.  
  20. Variable Lists are handled as separate objects, and the address of the
  21. object must be installed into the evaluator object befor using any
  22. variable stuff.
  23.  
  24. An external process (see the evaluator test program) must set up and
  25. remove variables.  I changed this from Neil's auto variable because I
  26. really hate auto variables, and because out particular application would
  27. not allow them.  This could easily be overidden if desired.
  28.  
  29. NOTE:  The program does not handle repeated unary operators w/o
  30. parentheses -- "---1" is bad, "-(-(-1))" is good.
  31.  
  32. The process operates with an object called EVAL_TYPE which allows for
  33. multiple concurrent instances of the evaluator.
  34.  
  35. One entry will return a real, while the other completely solves
  36. for a variable to the left of the = and updates the variable.
  37.  
  38. Hooks are provided via virtual methods to allow for external
  39. functions that take a single real variable.
  40.  
  41. ---------------------------------------------------------------------------
  42. HELP!  I would like to add the ability to have multiple argument
  43. functions rather than single variables ones like we have here.  For example,
  44. external functions like MAX(A,B) or MYFUNC(A,B,3,C,d) would be great!
  45. ---------------------------------------------------------------------------}
  46.  
  47.  
  48. unit evaluate;
  49.  
  50. interface
  51.  
  52. {$define showit}
  53.  
  54. uses tpcrt, tpstring, testlib;
  55.  
  56. const
  57.   var_name_size= 12;  { one size fits all }
  58.  
  59. type
  60.  
  61.   any_var_name = string[var_name_size];  { typical variable name }
  62.  
  63.   a_var_entry = record        { a single entry in the var list }
  64.    var_name : string[12];  { always convert to upper case }
  65.    var_value : real;    { only support reals for now }
  66.   end;
  67.  
  68.  var_entry_list = array [1..1] of a_var_entry;  { a group of entries }
  69.  
  70.  A_var_list = object     { one group of variable entries }
  71.    var_count : integer; {max number in the list}
  72.    var_list  : ^var_entry_list;  { points to the variable list somewhre }
  73.  
  74.    constructor init(c:integer);   { how many will be in the list }
  75.    function add_name( n : any_var_name; var index : integer) : boolean; virtual;
  76.    procedure delete_name(n:any_var_name);                         virtual;
  77.    function get_index(n : any_var_name; var index : integer): boolean; virtual;
  78.    function get_by_name(n : any_var_name; var rx : real;
  79.                         var index : integer ) : boolean;          virtual;
  80.    function get_by_index(n : integer; var rx : real ) : boolean;  virtual;
  81.    function set_by_name(n : any_var_name;  rx : real;
  82.                         var index : integer ) : boolean;          virtual;
  83.    function set_by_index(n : integer;  rx : real ) : boolean;     virtual;
  84.  end;
  85.  
  86.  
  87.  a_var_list_p = ^a_var_list;  { pointer so object can be passed around }
  88.  
  89.  
  90. Int_Var = RECORD            {Internal Variable type}
  91.   varName : string[8];  { max of 8 chars for variable names }
  92.   value : Real;
  93. end;
  94.  
  95.  
  96. eval_type = object
  97.   vTop, B_Dummy, B_Val : integer;
  98.   eval_result : Real;        { what happens in the end }
  99.   eval_err : Boolean;        { an error occured in evaluating }
  100.   var_error : boolean;       { an error occued in the variable declaration}
  101.   need_monitor : boolean;    { true if progress monitor needed}
  102.   a_v_l   : a_var_list_p;    { points to current variable list }
  103.  
  104.   constructor init;
  105.   procedure show_progress(m : small_string); virtual;
  106.   procedure show_text(m : med_string);  virtual;
  107.   procedure set_var_list(avl : a_var_list_p);  { set up the variabvle list }
  108. { hooks to externals }
  109.   procedure show_error(m : med_string;  var go_on : boolean); virtual;
  110.   function ext_fun_execute(i : integer; rx : real) : real; virtual;
  111.   function ext_fun_search(s : small_string) : integer;   virtual;
  112.  
  113.   PROCEDURE set_variable(var L : small_string);
  114.  
  115.   function solve(EL : string) :boolean;  { solve into RR }
  116.  
  117.   function do_evaluate(var  line : string) : boolean;
  118.  
  119. end;
  120. eval_type_ptr = ^eval_type;
  121.  
  122.  
  123. implementation
  124.   uses printf;
  125.  
  126.  
  127. const
  128.   nl = acr+alf;
  129.  
  130. TYPE
  131.  
  132.   char_set = SET of Char;
  133.  
  134.   treePtr   = ^node;
  135.   whichType = (lef,rit,mid,bra,mor);  { which type of element we are parsing}
  136.   tagtype   = (valuop, unop, bnop,funop );
  137.   binop_type =(badd,bsub,bmul,bdiv,
  138.                bpwr,     { power }
  139.                bor,      { logical or }
  140.                band,     { logical and }
  141.                beq,      { equals }
  142.                bgt,      { > }
  143.                blt,      { < }
  144.                blteq,    {<= }
  145.                bgteq);  { >=}
  146.  
  147.   una_type  = (posit,neg,intg,
  148.               fun_bogus,fun_external, fun_sqrt, fun_sqr,fun_int,fun_frac,
  149.               fun_pi,fun_abs, fun_round,fun_sin,fun_cos,fun_arctan,fun_tan,
  150.               fun_trunc);
  151.  
  152.  
  153. node = record
  154.   case tag : tagtype of
  155.     valuop : (value : real);
  156.     unop   : (UnaOp : una_type; branch : TreePtr;
  157.               external_code : integer);
  158.     bnop   : (left : treePtr; BinOp : binop_type;
  159.               right  : treePtr  );
  160.  end;
  161.  
  162.  
  163.  
  164. CONST
  165.   numbers : char_set = ['0'..'9', '.'];
  166.   alphas  : char_set = ['A'..'Z', 'a'..'z'];
  167.   delis : char_set = [' ', '+', '-', '(', '[', ')', ']', '*', '/','^',
  168.                       '>', '<', '=','&','|'];
  169.  
  170.  
  171.   alpha_code = #1;
  172.  
  173. { ------------  VARIABLE PROCESSING METHODS ------------------}
  174.  
  175.  
  176.  
  177. { call after memory is allocated for this unit so it cal further allocate
  178.   memory for the actual variable space.  The object itself only contains
  179.   the control information }
  180. constructor a_var_list.init(c:integer);
  181. var i : integer;
  182. begin
  183.   var_count := c;  { how many elements are in this particular instance }
  184.   getmem(var_list, c * sizeof(a_var_entry));  { space for actual variables}
  185.   for i := 1 to c do with var_list^[i] do begin
  186.     var_name := '';  var_value := 0;  { empty slots have '' name }
  187.   end;
  188. end;
  189.  
  190.  
  191. { for speed assume that all variables are passed here in UPCASE}
  192.  
  193. { Get the index into this object list for a particular variable.
  194.   This is handy to speed things up in later processing as well as
  195.   allowing the location rather than the name to be saved}
  196.  
  197. function a_var_list.get_index(n : any_var_name; var index : integer) :boolean;
  198. var
  199. i : integer;  ready : boolean;
  200.  
  201. begin
  202.   index := 0;  { assume no index available }
  203.   i := 1;  ready := false;   get_index := false; { assume no match}
  204.   while not ready do with var_list^[i] do begin
  205.     if var_name = n then begin
  206.       ready := true;  index := i;  get_index := true;
  207.     end;
  208.     inc(i);  if i > var_count then ready := true;
  209.   end;
  210. end;
  211.  
  212.  
  213. { Match var will look for a variable and if found will return
  214.   its value in RX}
  215.  
  216. { add a new name to the list if it does not exist.  If it is already
  217.   there, just return true }
  218.  
  219. function a_var_list.add_name( n : any_var_name; var index : integer) : boolean;
  220.  
  221. var I : integer;
  222. ready : boolean;
  223. rx2 : real;
  224.  
  225. begin
  226.   add_name := true;  { assume already existing }
  227.   if get_by_name(n, rx2, index) then exit;
  228.   i := 1;   ready := false;
  229.   repeat
  230.     with var_list^[i] do begin
  231.       if var_name = '' then begin     { found blank spot }
  232.         index := i;  { return new position in the list }
  233.         var_name := n;   ready := true;
  234.         exit;  { leave with true result }
  235.       end;
  236.     end;
  237.     inc(i);  if i > var_count then ready := true;
  238.   until ready;
  239.   add_name := false;  { could not make room }
  240. end;  { adding a new one }
  241.  
  242.  
  243. function a_var_list.get_by_index(n : integer; var rx : real ) : boolean;
  244. begin
  245.   get_by_index := false;
  246.   if (n <=0 ) or (n > var_count) then exit;
  247.   rx := var_list^[n].var_value;
  248.   get_by_index := true;
  249. end;
  250.  
  251.  
  252. function a_var_list.get_by_name(n : any_var_name; var rx : real;
  253.                                 var index : integer ) : boolean;
  254. begin
  255.   get_by_name := false;
  256.   if get_index(n, index) then
  257.   if get_by_index(index, rx) then get_by_name := true;
  258.  
  259. end;
  260.  
  261.  
  262. function a_var_list.set_by_index(n : integer; rx : real ) : boolean;
  263. begin
  264.   set_by_index := false;
  265.   if (n <=0 ) or (n > var_count) then exit;
  266.   var_list^[n].var_value := rx;
  267.   set_by_index := true;
  268. end;
  269.  
  270. function a_var_list.set_by_name(n : any_var_name;  rx : real;
  271.                                 var index : integer ) : boolean;
  272. begin
  273.   set_by_name := false;
  274.   if get_index(n, index) then  { get the index for this name }
  275.   if set_by_index(index, rx) then set_by_name := true;
  276. end;
  277.  
  278.  
  279. { remove a variable when we are thru with it }
  280. procedure a_var_list.delete_name(n:any_var_name);
  281.  
  282. var i : integer;
  283. ready : boolean;
  284. begin
  285.   i := 1;  ready := false;
  286.   repeat
  287.     with var_list^[i] do begin
  288.       if var_name = n then begin     { found it }
  289.         var_value:= 0;   ready := true;
  290.       end;
  291.     end;
  292.     inc(i);  if i > var_count then ready := true;
  293.   until ready;
  294. end;
  295.  
  296.  
  297. PROCEDURE eval_type.show_text(M : med_string);
  298. begin
  299.   write(m);
  300. end;
  301.  
  302. { show error and kill further operations }
  303. procedure eval_type.show_error(m : med_string;  var go_on : boolean);
  304. begin
  305.   show_text(m);
  306.   go_on:= false;
  307. end;
  308.  
  309. { This allows monitorong of the calculations }
  310.  
  311. procedure eval_type.show_progress(m: small_string);
  312. begin
  313.   if need_monitor then show_text(m);
  314. end;
  315.  
  316. PROCEDURE eval_type.set_var_list(avl : a_var_list_p);
  317. begin
  318.   a_v_l := avl;
  319. end;
  320.  
  321.  
  322. PROCEDURE StripOut(CH : Char; var LL : med_string);
  323.   (************************************************)
  324.   (*  Strip all occurrences of the character CH   *)
  325.   (*  out of the string LL.                       *)
  326.   (************************************************)
  327. begin
  328.   while Pos(CH, LL) <> 0 do Delete(LL, Pos(CH, LL), 1);
  329. end;
  330.  
  331. { -------------------------------------------------------
  332.   This is entry with EL containg the expression to evaluate.
  333.   The result goes into eval_result, boolean result indicates errors.
  334.   --------------------------------------------------------}
  335.  
  336. FUNCTION eval_type.solve(EL : string) : boolean;
  337. var
  338.   NumStr : med_string;
  339.   C, N : Integer;
  340.   evald : Boolean;
  341.  
  342.  
  343.  
  344. PROCEDURE NewEval(LL : med_string; var evR : real; var OK : Boolean);
  345.  
  346. var
  347.   Rut  : treePtr;
  348.   code : Integer;
  349.  
  350.  
  351. PROCEDURE Into_Tree(S : med_string; Root : treePtr; a_v_l: a_var_list_p );
  352. var
  353.   ii : integer;
  354.   temp : treePtr;
  355.   item : med_string;
  356.   which, holdwhich : whichType;
  357.   a_variable : boolean;
  358.   temp_op : una_type;  { temporary unary operator type }
  359.   temp_code : integer; { temo external code number }
  360.  
  361.  
  362. function Type_of(N : node):CHAR;
  363. begin
  364.   case N.tag of
  365.     valuop : Type_of := 'V';
  366.     unop : Type_of := 'U';
  367.     bnop : Type_of := 'B';
  368.     else Type_of := '?';
  369.   end;
  370. end;  { type_of }
  371.  
  372.  
  373.  
  374. FUNCTION letter(W : whichType):char;
  375. begin
  376.   case W of
  377.     lef: letter := 'L';
  378.     rit: letter := 'R';
  379.     mid: letter := 'M';
  380.     mor: letter := 'X';
  381.     bra: letter := 'B';
  382.   end;
  383. end;  { letter }
  384.  
  385.  
  386. PROCEDURE Put_Temp_In_Place;
  387. begin
  388.   case which of
  389.     lef : begin
  390.       Root^.tag := bnop;
  391.       Root^.Left := temp;
  392.       which := mid;
  393.     end;
  394.  
  395.     rit : begin
  396.       Root^.right := temp;
  397.       which := mor;
  398.     end;
  399.  
  400.     mid, mor : begin
  401.       show_error(nl+'error in format!'+nl, ok);
  402.     end;
  403.  
  404.     bra : begin
  405.       case HoldWhich of
  406.         mid : Root^.left^.branch := temp;
  407.         mor : Root^.right^.branch := temp;
  408.         bra : Root^.branch^.branch := temp;
  409.         else show_error(nl+'Error with unary operator '+letter(HoldWhich)+nl,ok);
  410.       end;
  411.       which := HoldWhich;
  412.     end;
  413.   end;
  414. end;  { put temp in place }
  415.  
  416.  
  417. PROCEDURE SplitOff(var SS, SItem : med_string; var ok : boolean);
  418. var
  419.   N, P, Parens : Byte;
  420.  
  421. begin
  422.   N := 1;
  423.   while S[N] = ' ' do N := N+1;  { skip white space }
  424.  
  425.   case s[n] of
  426.     '(' : begin
  427.       P := N;
  428.       Parens := 1;
  429.       repeat
  430.         P := P+1;
  431.         if S[P] = '(' then Parens := Parens+1;
  432.         if S[P] = ')' then Parens := Parens-1;
  433.       until (Parens = 0) OR (P = Length(S));
  434.       if Parens <> 0 then
  435.         begin
  436.           show_error(nl+'Error -- no right ('+nl, ok);
  437.         end
  438.       else
  439.         begin
  440.           sitem := Copy(S, N, P-N+1);
  441.           Delete(S, 1, P);
  442.         end;
  443.     end;
  444.  
  445.     '[' : begin
  446.       P := N;
  447.       Parens := 1;
  448.       repeat
  449.         P := P+1;
  450.         if S[P] = '[' then Parens := Parens+1;
  451.         if S[P] = ']' then Parens := Parens-1;
  452.       until (Parens = 0) OR (P = Length(S));
  453.       if Parens <> 0 then
  454.         begin
  455.           show_error(nl+'Error -- no right ]'+nl, ok);
  456.         end
  457.       else begin
  458.         sitem := Copy(S, N, P-N+1);
  459.         Delete(S, 1, P);
  460.       end;
  461.     end;
  462.  
  463. { Modified variable declaration.  May also be a function}
  464.  
  465.     'A'..'Z','a'..'z' : begin
  466.       P := N;  { where to start looking for the variable }
  467.       repeat
  468.         P := P+1   { advance to next delimiter }
  469.       until S[P] IN delis;
  470.       sitem := Copy(S, N, P-N);  { pull out the variable name }
  471.       sitem := stupcase(item);  { always use upper case here }
  472.       Delete(S, 1, P-1);  { strip out the variable }
  473.     end;
  474.  
  475.     '+', '-', '*', '/', '%','^',
  476.     '>', '<', '=','&','|' : begin   { math operator of some sort }
  477.       sitem := S[N];
  478.       Delete(S, 1, N);  { This seems confused by ^ ahead of number }
  479.     end;
  480.  
  481.     '0'..'9' : begin         { a number of some sort }
  482.       P := N;
  483.       repeat P := P+1 until NOT(S[P] IN numbers);
  484.       sitem := Copy(S, N, P-N);
  485.       Delete(S, 1, P-1);
  486.  
  487.     end;
  488.  
  489.     else show_error(nl+'Not a valid character here '+S[N]+nl, ok);
  490.   end;                  {case}
  491. end;                    {SplitOff}
  492.  
  493.  
  494. begin  { start of into_tree }
  495.   with a_v_l^ do begin  { give us access to the external variable list }
  496.   which := lef;  { looking for a left thing }
  497.  
  498.   ok := True;
  499.   while (S[0] > #0) and ok do begin   { something left on the line }
  500.     SplitOff(S, item,ok);  { take next item FROM S }
  501.     item := stupcase(item);
  502.     a_variable := true;       { let variables run if no function found }
  503.  
  504.     case item[1] of
  505.       '0'..'9' : begin
  506.         New(temp);
  507.         temp^.tag := valuop;
  508.         Val(item, temp^.value, code);  { convert string to number }
  509.         if code <> 0 then
  510.           show_error(nl+'Invalid numeric format '+item+nl, ok)
  511.         else Put_Temp_In_Place;
  512.       end;
  513.  
  514.  
  515.       '(', '[' : begin
  516.           item[0] := Pred(item[0]);
  517.           item[1] := ' ';
  518.           New(temp);
  519.           Into_Tree(item, temp,a_v_l);
  520.           Put_Temp_In_Place;
  521.         end;
  522.  
  523.  
  524. { Check for possible function, then check for variable }
  525.        'A'..'Z','a'..'z',
  526.        '@' : begin   { special function is like a unary operator }
  527. { before processing, we need to find out what kind of function this is }
  528.  
  529.          temp_op := fun_bogus;         { assume no function match at all }
  530.  
  531.          if item = 'SQR'    then temp_op := fun_sqr;
  532.          if item = 'SQRT'   then temp_op := fun_sqrt;
  533.          if item = 'INT'    then temp_op := fun_int;
  534.          if item = 'FRAC'   then temp_op := fun_frac;
  535.          if item = 'PI'     then temp_op := fun_pi;
  536.          if item = 'ABS'    then temp_op := fun_abs;
  537.          if item = 'ROUND'  then temp_op := fun_round;
  538.          if item = 'SIN'    then temp_op := fun_sin;
  539.          if item = 'COS'    then temp_op := fun_cos;
  540.          if item = 'TAN'    then temp_op := fun_tan;
  541.          if item = 'ARCTAN' then temp_op := fun_arctan;
  542.          if item = 'TRUNC'  then temp_op := fun_trunc;
  543.  
  544.          if temp_op = fun_bogus then begin { external functions }
  545.            temp_code :=0;  { asssume no match }
  546.             temp_code := ext_fun_search(item);  { look for an external funct}
  547.             if temp_code <> 0 then begin { one was found }
  548.               temp_op := fun_external;
  549.             end;
  550.          end;
  551.  
  552.          if temp_op = fun_bogus then a_variable := true
  553.          else a_variable := false;
  554.  
  555.          if not a_variable  then begin  { some sort of  function found }
  556.            case which of
  557.              lef, rit, bra : begin
  558.                new(temp);        { get another block of memory }
  559.                with temp^ do begin
  560.                  tag := unop;  { identify as a function block}
  561.                  unaop := temp_op; { what kind of function }
  562.                  external_code := temp_code; { if an external, which one}
  563.                end;
  564.                Put_Temp_In_Place;
  565.                HoldWhich := which;
  566.                which := bra;
  567.              end;  { lef, rit, bra }
  568.              mor : begin  { middle or right }
  569.                New(temp);
  570.                temp^ := root^;
  571.                root^.tag := bnop;
  572.                root^.binop := bsub;
  573.                root^.left := temp;
  574.                which := rit;
  575.              end;  { mor}
  576.              mid : begin
  577.                root^.binop := bsub;
  578.                which := rit;
  579.              end;  { mid}
  580.              else show_error(nl+'Bad Function= '+nl, ok);
  581.            end;  { case which }
  582.          end;  { setting up a function }
  583.  
  584.          if a_variable then begin
  585.            new(temp);
  586.            with temp^ do begin
  587.              tag := valuop;  { indicate it's a value }
  588. (*
  589.              get_var_value(item, value, self);
  590. *)
  591.              if get_by_name(item, value, ii) then
  592.              Put_Temp_In_Place;
  593.           end;
  594.          end;  { setting up a variable}
  595.         end;
  596.  
  597.        '%' : begin
  598.          new(temp);
  599.          temp^.tag := UnOp;
  600.          temp^.unaop := intg;
  601.          Put_Temp_In_Place;
  602.          HoldWhich := which;
  603.          which := bra;
  604.        end;
  605.  
  606.        '-' : begin
  607.          case which of
  608.            lef, rit, bra : begin
  609.              new(temp);
  610.              temp^.tag := UnOp;
  611.              temp^.UnaOp := neg;
  612.              Put_Temp_In_Place;
  613.              HoldWhich := which;
  614.              which := bra;
  615.            end;
  616.            mor : begin
  617.              New(temp);
  618.              temp^ := root^;
  619.              root^.tag := bnop;
  620.              root^.binop := bsub;
  621.              root^.left := temp;
  622.              which := rit;
  623.            end;
  624.            mid : begin
  625.              root^.binop := bsub;
  626.              which := rit;
  627.            end;
  628.           else show_error(nl+'Bad expression'+nl, ok);
  629.         end;
  630.       end;  { - }
  631.  
  632.  
  633.       '+', '*', '/', '^','>','<','=',
  634.       '&', '|' : begin   { binary operations follow}
  635.         case which of
  636.           mid : begin  { looking for the middle operator }
  637.             case item[1] of
  638.               '+' : root^.binop := badd;
  639.               '*' : root^.binop := bmul;
  640.               '/' : root^.binop := bdiv;
  641.               '^' : root^.binop := bpwr;
  642.               '>' : root^.binop := bgt;
  643.               '<' : root^.binop := blt;
  644.               '=' : root^.binop := beq;
  645.               '&' : root^.binop := band;
  646.               '|' : root^.binop := bor;
  647.             end;
  648.             which := rit;  { better find a right operand }
  649.           end;  { mid }
  650.  
  651.           mor : begin  { middle or right operator }
  652.             New(temp);
  653.             temp^ := root^;
  654.             root^.tag := bnop;
  655.             case item[1] of
  656.               '+' : root^.binop := badd;
  657.               '*' : root^.binop := bmul;
  658.               '/' : root^.binop := bdiv;
  659.               '^' : root^.binop := bpwr;
  660.               '>' : root^.binop := bgt;
  661.               '<' : root^.binop := blt;
  662.               '=' : root^.binop := beq;
  663.               '|' : root^.binop := bor;
  664.               '&' : root^.binop := band;
  665.             end;
  666.             root^.left := temp;
  667.             which := rit;  { should be more to come }
  668.           end;
  669.           else show_error(nl+'Error in format'+nl, ok);
  670.         end;  { case }
  671.       end;  { '+', '*', '/','^' }
  672.  
  673.  
  674.     end;                {case}
  675.   end;                  {while}
  676.  
  677.   case which of
  678.     rit : show_error(nl+'Second operand missing!'+nl, ok);
  679.     mid : begin
  680.       temp := root^.left;
  681.       root^ := root^.left^;
  682.       dispose(temp);
  683.     end;
  684.     bra : show_error(nl+'Unary operand missing+nl', ok);
  685.     lef : show_error(nl+'Left side missing?'+nl, ok);
  686.     mor :;
  687.   end;
  688.   end;  {a ccess to variable list object }
  689. end;
  690.  
  691.  
  692. { This will reduce a tree node by doing the math as necessary }
  693.  
  694. FUNCTION ExprValue(T : treePtr) : Real;
  695. var   v1, v2 : Real;
  696. rx : real;
  697. begin
  698.   case T^.tag of
  699.     valuop : begin       { end of the tree, say the number }
  700. {$ifdef showit}
  701.       if need_monitor then show_progress(fmtreal('d2',t^.value)+'= ');
  702. {$endif}
  703.       ExprValue := T^.value;
  704.     end;
  705.  
  706.     unop : begin  { a unary operation }
  707. {$ifdef showit}
  708.       if t^.unaop > fun_bogus then
  709.       if need_monitor then show_progress(fmtreal('d2',t^.value)+' (F) ');
  710. {$endif}
  711.  
  712.       v1 := exprValue(T^.branch);
  713.       case T^.UnaOp of
  714.         fun_external : with t^ do begin
  715. {$ifdef showit}
  716.       if need_monitor then show_progress(fmtreal('d2',t^.value)+'-X- ');
  717. {$endif}
  718.           exprvalue := ext_fun_execute(external_code,v1);
  719.         end;
  720.         fun_sqrt  : exprvalue := sqrt(v1);
  721.         fun_sqr   : exprvalue := sqr(v1);
  722.         fun_int   : exprvalue := int(v1);
  723.         fun_frac  : exprvalue := frac(v1);
  724.         fun_pi    : exprvalue := pi * v1;
  725.         fun_abs   : exprvalue := abs(v1);
  726.         fun_round : exprvalue :=  round(v1);
  727.         fun_sin   : exprvalue :=  sin(v1);
  728.         fun_cos   : exprvalue :=  cos(v1);
  729.  
  730.         fun_tan : begin
  731.           rx := cos(v1);
  732.           if rx <> 0 then exprvalue :=  sin(v1) / cos(v1)
  733.           else exprvalue := 0;
  734.         end;
  735.         fun_arctan : exprvalue :=  arctan(v1);
  736.         fun_trunc  : exprvalue :=  trunc(v1);
  737.  
  738. { Normal unary options follow }
  739.         posit : begin
  740.           exprvalue := v1;
  741.         end;
  742.  
  743.         neg : begin
  744.           exprValue := -v1;
  745. {$ifdef showit}
  746.           if need_monitor then show_progress('- ');
  747. {$endif}
  748.         end;
  749.         intg : begin         { chop to an integer }
  750.           exprValue := Trunc(v1);
  751. {$ifdef showit}
  752.           if need_monitor then show_progress('% ');
  753. {$endif}
  754.         end;
  755.       end;
  756.     end;
  757.  
  758.     bnop : begin
  759.       v1 := exprValue(T^.left);
  760.       v2 := exprValue(T^.right);
  761.       case T^.binop of
  762.         badd : begin
  763.           exprValue := v1+v2;
  764. {$ifdef showit}
  765.           if need_monitor then show_progress('+ ');
  766. {$endif}
  767.         end;
  768.         bsub : begin
  769.           exprValue := v1-v2;
  770. {$ifdef showit}
  771.           if need_monitor then show_progress('- ');
  772. {$endif}
  773.         end;
  774.         bmul : begin
  775.           exprValue := v1*v2;
  776. {$ifdef showit}
  777.           if need_monitor then show_progress('* ');
  778. {$endif}
  779.         end;
  780.         bdiv : begin
  781.           if v2 <> 0 then
  782.           exprValue := v1/v2
  783.           else exprvalue := realbig;  { avoid errors }
  784. {$ifdef showit}
  785.           if need_monitor then show_progress('/ ');
  786. {$endif}
  787.         end;
  788.         bpwr : begin
  789.           exprValue := power(v1,v2);  { DATARAN or TEST library function}
  790. {$ifdef showit}
  791.           if need_monitor then show_progress('^ ');
  792. {$endif}
  793.         end;
  794.  
  795.         blt : begin  { if V1 < v2  return 1 }
  796.           if v1 < v2 then
  797.           exprValue :=  1 else exprvalue := 0;
  798.         end;
  799.  
  800.         bgt : begin  { if V1 > v2  return 1 }
  801.           if v1 > v2 then
  802.           exprValue :=  1 else exprvalue := 0;
  803.         end;
  804.  
  805.         beq : begin  { if V1 = v2  return 1 }
  806.           if v1 = v2 then
  807.           exprValue :=  1 else exprvalue := 0;
  808.         end;
  809.         bor : begin  { if V1 or v2  return 1 }
  810.           if trunc(v1) + trunc(v2) <> 0 then
  811.           exprValue :=  1 else exprvalue := 0;
  812.         end;
  813.         band : begin  { if V1 AND v2  return 1 }
  814.           if trunc(v1) * trunc(v2) <> 0 then
  815.           exprValue :=  1 else exprvalue := 0;
  816.         end;
  817.  
  818.  
  819.       end;
  820.     end;
  821.   end;
  822.   Dispose(T);
  823. end;  { of expr value }
  824.  
  825.  
  826. begin  { neweval start }
  827.  
  828.   New(RUT);
  829.   Into_Tree(LL, Rut,a_v_l);
  830.   if OK then begin
  831.  
  832. {$ifdef showit}
  833.     show_progress(acr+alf);
  834.     show_progress('RPN: ');
  835. {$endif}
  836.  
  837.     evR := ExprValue(RUT);
  838.   end;
  839.  
  840. end; { end of neweval}
  841.  
  842. begin    { solve start }
  843.   NewEval(EL, eval_result,evald);
  844.   solve := evald;
  845. end;  { of solve }
  846.  
  847.  
  848. { Assign the resulting expression to the variable in sting.
  849.   This entry is used if a variable is begin updated (or created)
  850.   at the start of the main entry line.
  851. }
  852.  
  853. PROCEDURE eval_type.set_variable(var L : small_string);
  854. var
  855.   VarStr : med_string;          {expression of var's value -- from "=" to end}
  856.   VarNm : small_string;            {name of var -- from 2nd char to "="         }
  857.   N : Byte;
  858.   hit : boolean;
  859.  treal : real;
  860.  is_external : boolean;
  861.  ok : boolean;
  862.  index : integer;
  863.   ii : integer;
  864. begin
  865.   with a_v_l^ do begin
  866.   is_external := false;  { assume a local variable }
  867.  
  868.   if Pos('=', L) <> 0 then begin  { an assignment is being made }
  869.     VarNm := Copy(L, 1, Pos('=', L)-1);  { first part of the line }
  870.     varnm := stupcase(varnm);  { only look at uppers }
  871.  
  872.     StripOut(' ', VarNm);
  873.     Delete(L, 1, Pos('=', L));  { remove first part of the line }
  874.     VarStr := L;  { rest of the line that will be solved }
  875.  
  876.     hit := false;  { no find as yet }
  877.  
  878.     hit := get_by_name(varnm,treal,index);  { if there, return true and value}
  879.  
  880.     if hit then begin  { found the variable }
  881.       if solve(VarStr) then      { solve the expression }
  882.         if not set_by_name(varnm,eval_result,ii) then begin
  883.  
  884.         show_error(nl+'Error in line '+nl, ok);
  885.         show_error(nl+'Variable Def error= '+ VarStr+nl, ok);
  886.         var_error := True;
  887.       end;
  888.     end;
  889.   end  { if no error in the initial variable setup }
  890.  
  891.   else begin  { if an error in setting up the variable }
  892.     show_progress(acr+alf+'No "=" in variable definition.'+acr+alf);
  893.     var_error := True;
  894.   end;
  895.   end;  { access to a_v_l }
  896. end;  { of set var }
  897.  
  898.  
  899.  
  900. { This is the external hook into the solver.  Call it with the string,
  901.   and it will return a real.  In the structure.
  902.   The boolean result indicates error.
  903.   if it is just an expression (i.e. no = ), then the real result
  904.   is calculated if possible.  if a Variable is being solved for at the
  905.   left of the =, it is updated with the new value
  906. }
  907.  
  908. function eval_type.do_evaluate(var  line : string) : boolean;
  909. var well : boolean;
  910.  
  911. begin
  912.     do_evaluate := false;
  913.     var_error := false;
  914.     well := true;
  915.     if pos('=',line) <> 0 then
  916.       set_variable(line)  { solve for a variable to left of =}
  917.     else
  918.       well := solve(line);  { solve and return results in struct}
  919.  
  920.     if not well then
  921.     show_progress(acr+alf+'Error in expression!'+acr+alf);
  922.     do_evaluate := well;
  923. end;
  924.  
  925.  
  926.  
  927. { Call to initiate a solving session }
  928. constructor eval_type.init;
  929. begin
  930.   vtop := 0;
  931.   var_error := false;
  932.   eval_err := false;
  933.   need_monitor := false;
  934.   a_v_l := nil; { assume no variable list has been set up }
  935. end;
  936.  
  937.  
  938. { The HOOKS below allow access to external functions.  The string name
  939.   is the function, and the search should return 0 if no external
  940.   function is found.  If it is found, return an index number.  This number
  941.   will be sent later at evaluate time so the external function processor
  942.   knows what to do to the real being passed to it.
  943. }
  944.  
  945. function eval_type.ext_fun_search(s : small_string) : integer;
  946. begin
  947.   ext_fun_search := 0;
  948. end;
  949.  
  950. { Use the index code I to act on RX, return a real result }
  951. function eval_type.ext_fun_execute(i : integer; rx : real) : real;
  952. begin
  953.   ext_fun_execute := 0;
  954. end;
  955.  
  956.  
  957. end.
  958.